package com.practice.concurrent;


import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.LockSupport;

/**
 * AQS,AbstractQueueSynchronizer
 * AQS 是一个抽象类，为同步器提供了通用的 执行框架。它定义了资源获取和释放的通用流程
 *AQS核心思想是，如果被请求的共享资源空闲，则将当前请求资源的线程设置为有效的工作线程，并且将共享资源设置为锁定状态。
 * 如果被请求的共享资源被占用，那么就需要一套线程阻塞等待以及被唤醒时锁分配的机制，
 * 这个机制 AQS 是基于 CLH 锁 （Craig, Landin, and Hagersten locks） 进一步优化实现的。
 * --
 * CLH 锁对自旋锁进行了改进，是基于单链表的自旋锁。在多线程场景下，会将请求获取锁的线程组织成一个单向队列，每个等待的线程会通过自旋访问前一个线程节点的状态，前一个节点释放锁之后，当前节点才可以获取锁。
 *
 * AQS中使用的 待队列 是 CLH 锁队列的变体（接下来简称为 CLH 变体队列）。
 * AQS 的 CLH 变体队列是一个双向队列，会暂时获取不到锁的线程将被加入到该队列中，CLH 变体队列和原本的 CLH 锁队列的区别主要有两点：
 * 由 自旋 优化为 自旋 + 阻塞 ：自旋操作的性能很高，但大量的自旋操作比较占用 CPU 资源，因此在 CLH 变体队列中会先通过自旋尝试获取锁，如果失败再进行阻塞等待。
 * 由 单向队列 优化为 双向队列 ：在 CLH 变体队列中，会对等待的线程进行阻塞操作，当队列前边的线程释放锁之后，需要对后边的线程进行唤醒，因此增加了 next 指针，成为了双向队列。AQS 将每条请求共享资源的线程封装成一个 CLH 变体队列的一个结点（Node）来实现锁的分配。在 CLH 变体队列中，一个节点表示一个线程，它保存着线程的引用（thread）、 当前节点在队列中的状态（waitStatus）、前驱节点（prev）、后继节点（next）。
 */
public class AQSLock {

    AtomicBoolean flag = new AtomicBoolean(false);

    // 实现可重入锁
    AtomicInteger state = new AtomicInteger(0);

    Thread owner = null;

    class Node{
        Node next;
        Node pre;
        Thread thread;
    }

    AtomicReference<Node> head = new AtomicReference<Node>(new Node());

    AtomicReference<Node> tail = new AtomicReference<>(head.get());

    void lock(){
        // 添加可重入锁的实现
        if(state.get() == 0){
            //这里会出现非公平锁现象，没有严格按照加入链表的顺序进行分配锁
            if(state.compareAndSet(0,1)){
                System.out.println(Thread.currentThread().getName()+"have lock");
                owner = Thread.currentThread();
                return;
            }
        }else{
            if(owner == Thread.currentThread()){
                System.out.println(Thread.currentThread().getName()+"have Reentrant lock" + state.incrementAndGet());
                return;
            }
        }

//        //这里会出现非公平锁现象，没有严格按照加入链表的顺序进行分配锁
//        if(flag.compareAndSet(false,true)){
//            System.out.println(Thread.currentThread().getName()+"have lock");
//            owner = Thread.currentThread();
//            return;

        //将没拿到锁的线程存入链表
        Node current = new Node();
        current.thread = Thread.currentThread();
        while(true){
            Node currentTail = tail.get();
            if(tail.compareAndSet(currentTail,current)){
                System.out.println(current.thread.getName()+"join in List");
                current.pre = currentTail;
                currentTail.next = current;
                break;
            }
        }

        //对无锁线程进行阻塞
        while(true){
            //先判断能否获得锁，防止最后一个线程没有线程进行唤醒
//            if(current.pre == head.get() && flag.compareAndSet(false,true)){
            if(current.pre == head.get() && state.compareAndSet(0,1)){
                System.out.println(current.thread.getName()+"is parked and hava lock");
                owner = Thread.currentThread();
                head.set(current);
                current.pre.next = null;
                current.pre = null;
                return;
            }
            LockSupport.park();
        }
    }

    void unlock(){
        if(state.get() > 1){
            if(owner != Thread.currentThread()){
                throw new IllegalStateException("The Thread do not have lock");
            }
            state.decrementAndGet();
            System.out.println("Thread release lock, has surplus lock " + state.get());
            return;
        }
        else if(state.get() < 1){
            throw new IllegalStateException("Reentrant lock error");
        }

        Node headNode = head.get();
        Node next = headNode.next;
//        flag.set(false);
        state.set(0);
        if(next!=null){
            System.out.println("unparked " + next.thread.getName());
            LockSupport.unpark(next.thread);
        }
    }


}
